/*
 * Copyright (c) 2011, Yawning <yawninglol at gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   * Neither the name of the Mew Developers nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

package yawning.mew.equipment;

import java.net.URI;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;

import yawning.mew.equipment.RawItem.Property;
import yawning.mew.util.BattleNetAuth;
import yawning.mew.util.URLFetcher;

public class ArmoryItemSource extends ItemSource {
	public ArmoryItemSource() {
		// Nothing to do.  Yay.
	}

	private static final List<String> sHostsList = Arrays.asList(
			"us.battle.net",
			"eu.battle.net"
		);

	private static Map<Integer,Property> sStatIdPropertyMap;
	private static Map<String, Integer> sSocketWowheadTypeMap;

	static {
		sStatIdPropertyMap = new HashMap<Integer,Property>();
		sStatIdPropertyMap.put(7, Property.STAMINA);
		sStatIdPropertyMap.put(3, Property.AGILITY);
		sStatIdPropertyMap.put(4, Property.STRENGTH);
		sStatIdPropertyMap.put(5, Property.INTELLECT);
		sStatIdPropertyMap.put(6, Property.SPIRIT);
		sStatIdPropertyMap.put(45, Property.SPELL_POWER);
		sStatIdPropertyMap.put(38, Property.ATTACK_POWER);
		sStatIdPropertyMap.put(36, Property.HASTE_RATING);
		sStatIdPropertyMap.put(32, Property.CRIT_RATING);
		sStatIdPropertyMap.put(37, Property.EXPERTISE_RATING);
		sStatIdPropertyMap.put(31, Property.HIT_RATING);
		sStatIdPropertyMap.put(13, Property.DODGE_RATING);
		sStatIdPropertyMap.put(49, Property.MASTERY_RATING);
		sStatIdPropertyMap.put(14, Property.PARRY_RATING);
		sStatIdPropertyMap.put(35, Property.RESILIENCE_RATING);
		sStatIdPropertyMap.put(50, Property.BONUS_ARMOR);

		sSocketWowheadTypeMap = new HashMap<String,Integer>();
		sSocketWowheadTypeMap.put("META", 1);
		sSocketWowheadTypeMap.put("RED", 2);
		sSocketWowheadTypeMap.put("YELLOW", 4);
		sSocketWowheadTypeMap.put("BLUE", 8);
		sSocketWowheadTypeMap.put("PRISMATIC", 14);
		sSocketWowheadTypeMap.put("COGWHEEL", 32);
	}

	@Override
	protected RawItem getById(int id) {
		try {
			URI uri;
			URL url;
			String page = null;

			// Query battle.net for the item information
			// Eg: http://us.battle.net/api/wow/item/59474
			for (String host : sHostsList) {
				uri = new URI("http", null, host, 80, "/api/wow/item/" + id, "locale=en", null);
				url = new URL(uri.toASCIIString());

				// Optionally use authentication
				Map<String,String> reqProps = null;
				BattleNetAuth auth = BattleNetAuth.getInstance();
				if (auth.hasKeys()) {
					reqProps = auth.buildAuthRequestProps(uri);
				}

				try {
					page = URLFetcher.getInstance().fetchAsString(url, false, reqProps);
					break;
				} catch (Exception e) {
					// Try the next host in the list
					continue;
				}
			}

			if (page == null)
				throw new RuntimeException("Failed to obtain item data (" + id + ")");

//			System.out.println(page);

			JSONObject jso = (JSONObject)JSONValue.parse(page);
			RawItem item = new RawItem(id, (String) jso.get("name"));

			item.properties.put(Property.QUALITY, ((Number)jso.get("quality")).intValue());
			item.properties.put(Property.CLASS, ((Number)jso.get("itemClass")).intValue());
			item.properties.put(Property.SUBCLASS, ((Number)jso.get("itemSubClass")).intValue()); 	// Different from what wowhead returns for some items.
			item.properties.put(Property.ITEM_LEVEL, ((Number) jso.get("itemLevel")).intValue());
//			if (jso.containsKey("itemSet"))
//				item.properties.put(Property.ITEM_SET, ((Number) jso.get("itemSet")).intValue());	// Different from what wowhead returns for some items, and returns a string (WTF).

			item.properties.put(Property.ARMOR, ((Number)jso.get("baseArmor")).intValue());			// Some items have wrong values for this. (Druid T12 gloves)
			if (jso.containsKey("inventoryType"))
				item.properties.put(Property.SLOT, ((Number)jso.get("inventoryType")).intValue());

			JSONObject jso2;

			if (jso.containsKey("weaponInfo")) {
				jso2 = (JSONObject) jso.get("weaponInfo");

				int subclass = item.properties.get(Property.SUBCLASS).intValue();
				if (subclass == 2 || subclass == 3 || subclass == 15) {
					item.properties.put(Property.RANGED_SPEED, ((Number)jso2.get("weaponSpeed")).doubleValue());
					item.properties.put(Property.RANGED_DPS, ((Number)jso2.get("dps")).doubleValue());
					jso2 = (JSONObject) jso2.get("damage");
					item.properties.put(Property.RANGED_DMG_MIN, ((Number)jso2.get("min")).intValue());
					item.properties.put(Property.RANGED_DMG_MAX, ((Number)jso2.get("max")).intValue());
				} else {
					item.properties.put(Property.MELEE_SPEED, ((Number)jso2.get("weaponSpeed")).doubleValue());
					item.properties.put(Property.MELEE_DPS, ((Number)jso2.get("dps")).doubleValue());
					jso2 = (JSONObject) jso2.get("damage");
					item.properties.put(Property.MELEE_DMG_MIN, ((Number)jso2.get("min")).intValue());
					item.properties.put(Property.MELEE_DMG_MAX, ((Number)jso2.get("max")).intValue());
				}
			}

			if (jso.containsKey("bonusStats")) {
				JSONArray jsa = (JSONArray) jso.get("bonusStats");
				for (int i = 0; i < jsa.size(); i++) {
					jso2 = (JSONObject) jsa.get(i);
					int statId = ((Number) jso2.get("stat")).intValue();
					int statAmount = ((Number) jso2.get("amount")).intValue();

					if (sStatIdPropertyMap.containsKey(statId)) {
						item.properties.put(sStatIdPropertyMap.get(statId), statAmount);
					} else
						System.out.println("** ArmoryItemSource: StatId " + statId + " not yet supported");
				}
			}

			if (((Boolean) jso.get("hasSockets")) == true) {
				jso2 = (JSONObject) jso.get("socketInfo");
				JSONArray jsa = (JSONArray) jso2.get("sockets");
				int numSockets = jsa.size();
				item.properties.put(Property.NUM_SOCKETS, numSockets);
				if (numSockets >= 1) {
					jso2 = (JSONObject) jsa.get(0);
					String type = (String) jso2.get("type");
					item.properties.put(Property.SOCKET1, sSocketWowheadTypeMap.get(type));
				}
				if (numSockets >= 2) {
					jso2 = (JSONObject) jsa.get(1);
					String type = (String) jso2.get("type");
					item.properties.put(Property.SOCKET2, sSocketWowheadTypeMap.get(type));
				}
				if (numSockets >= 3) {
					jso2 = (JSONObject) jsa.get(2);
					String type = (String) jso2.get("type");
					item.properties.put(Property.SOCKET3, sSocketWowheadTypeMap.get(type));
				}
			} else {
				item.properties.put(Property.NUM_SOCKETS, 0);
			}

			// Missing/Not Set:
			// SOCKET_BONUS_STAT
			// SOCKET_BONUS_VALUE
			// Stats for gems in general.  FML.

			return item;
		} catch (Exception e) {
			sExceptions.append(e.toString());
			if (mNext != null) return mNext.getById(id);
			else return null;
		}
	}
}
